package com.nulldev.util.string;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.security.SecureRandom;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.DatatypeConverter;

import com.nulldev.util.FinalBypass;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.math.MathUtil;
import com.nulldev.util.other.ArrayUtils;
import com.nulldev.util.other.CharsetUtil;
import com.nulldev.util.other.RandomUtils.nSecureRandom;

public class StringsUtil {

	public static final String SPEECH_MARK = "\"";
	private static final Logger log = LoggerFactory.getLogger();
	public static int countChar(final String str, final char in) {
		int i = 0;
		for (char c : str.toCharArray()) {
			if (c == in) {
				i += 1;
			}
		}
		return i;
	}
	
	public static String capitalizeString(String string) {
		  char[] chars = string.toLowerCase().toCharArray();
		  boolean found = false;
		  for (int i = 0; i < chars.length; i++) {
		    if (!found && Character.isLetter(chars[i])) {
		      chars[i] = Character.toUpperCase(chars[i]);
		      found = true;
		    } else if (Character.isWhitespace(chars[i]) || chars[i]=='.' || chars[i]=='\'') {
		      found = false;
		    }
		  }
		  return String.valueOf(chars);
		}
	
	public static String setdoubleQuote(final String myText) {
	    String quoteText = "";
	    if (!myText.isEmpty()) {
	        quoteText = "\"" + myText + "\"";
	    }
	   
	    return quoteText;
	}
	
	public static String setdoubleC(final String myText) {
	    String quoteText = "";
	    if (!myText.isEmpty()) {
	        quoteText = "'" + myText + "'";
	    }
	   
	    return quoteText;
	}
	
	public static String findAndReplaceAll(String patterna, String replaceWith, String inputString) {
		    Pattern pattern = Pattern.compile(patterna);
		    Matcher matcher = pattern.matcher(inputString);
		    return matcher.replaceAll(replaceWith);
	}
	
	public static String findAndReplaceAll(final Pattern pattern, String replaceWith, String inputString) {
	    Matcher matcher = pattern.matcher(inputString);
	    return matcher.replaceAll(replaceWith);
}

    public static String substringBetween(final String str, final String tag) {
        return substringBetween(str, tag, tag);
    }

    public static String substringBetweenO(final String str, final String open, final String close) {
        if (str == null || open == null || close == null) {
            return null;
        }
        final int start = str.indexOf(open);
        if (start != -1) {
            final int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return null;
    }
    
    public static String substringBetween(final String str, final String open, final String close) {
        if (str == null || open == null || close == null) {
            return str;
        }
        final int start = str.indexOf(open);
        if (start != -1) {
            final int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return str;
    }
    
	public static String substringBetweenNA(final String str, final String open, final String close) {
        if (str == null || open == null || close == null) {
            return "N/A";
        }
        final int start = str.indexOf(open);
        if (start != -1) {
            final int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return "N/A";
	}
	
	public static String substringBetweenF(final String str, final String open, final String close, final String fallback) {
        if (str == null || open == null || close == null) {
            return fallback;
        }
        final int start = str.indexOf(open);
        if (start != -1) {
            final int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return fallback;
	}

    public static String reverse(String input){
        char[] in = input.toCharArray();
        int begin=0;
        int end=in.length-1;
        char temp;
        while(end>begin){
            temp = in[begin];
            in[begin]=in[end];
            in[end] = temp;
            end--;
            begin++;
        }
        return new String(in);
    }

    public static String[] split(final String str) {
        return split(str, null, -1);
    }

    public static String[] split(final String str, final char separatorChar) {
        return splitWorker(str, separatorChar, false);
    }

    public static String[] split(final String str, final String separatorChars) {
        return splitWorker(str, separatorChars, -1, false);
    }

    public static String[] split(final String str, final String separatorChars, final int max) {
        return splitWorker(str, separatorChars, max, false);
    }

    public static String[] splitByWholeSeparator(final String str, final String separator) {
        return splitByWholeSeparatorWorker(str, separator, -1, false);
    }

    public static String[] splitByWholeSeparator(final String str, final String separator, final int max) {
        return splitByWholeSeparatorWorker(str, separator, max, false);
    }

    public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator) {
        return splitByWholeSeparatorWorker(str, separator, -1, true);
    }

    public static String[] splitByWholeSeparatorPreserveAllTokens(final String str, final String separator, final int max) {
        return splitByWholeSeparatorWorker(str, separator, max, true);
    }

    private static String[] splitByWholeSeparatorWorker(final String str, final String separator, final int max, final boolean preserveAllTokens) {
        if (str == null) {
            return null;
        }
        final int len = str.length();
        if (len == 0) {
            return (String[]) ArrayUtils.EMPTY_STRING_ARRAY;
        }
        if (separator == null || "".equals(separator)) {
            return splitWorker(str, null, max, preserveAllTokens);
        }
        final int separatorLength = separator.length();
        final ArrayList<String> substrings = new ArrayList<String>();
        int numberOfSubstrings = 0;
        int beg = 0;
        int end = 0;
        while (end < len) {
            end = str.indexOf(separator, beg);
            if (end > -1) {
                if (end > beg) {
                    if (++numberOfSubstrings == max) {
                        end = len;
                        substrings.add(str.substring(beg));
                    }
                    else {
                        substrings.add(str.substring(beg, end));
                        beg = end + separatorLength;
                    }
                }
                else {
                    if (preserveAllTokens) {
                        if (++numberOfSubstrings == max) {
                            end = len;
                            substrings.add(str.substring(beg));
                        }
                        else {
                            substrings.add("");
                        }
                    }
                    beg = end + separatorLength;
                }
            }
            else {
                substrings.add(str.substring(beg));
                end = len;
            }
        }
        return substrings.toArray(new String[substrings.size()]);
    }

    private static String[] splitWorker(final String str, final char separatorChar, final boolean preserveAllTokens) {
        if (str == null) {
            return null;
        }
        final int len = str.length();
        if (len == 0) {
            return (String[]) ArrayUtils.EMPTY_STRING_ARRAY;
        }
        final List<String> list = new ArrayList<String>();
        int i = 0;
        int start = 0;
        boolean match = false;
        boolean lastMatch = false;
        while (i < len) {
            if (str.charAt(i) == separatorChar) {
                if (match || preserveAllTokens) {
                    list.add(str.substring(start, i));
                    match = false;
                    lastMatch = true;
                }
                start = ++i;
            }
            else {
                lastMatch = false;
                match = true;
                ++i;
            }
        }
        if (match || (preserveAllTokens && lastMatch)) {
            list.add(str.substring(start, i));
        }
        return list.toArray(new String[list.size()]);
    }

    private static String[] splitWorker(final String str, final String separatorChars, final int max, final boolean preserveAllTokens) {
        if (str == null) {
            return null;
        }
        final int len = str.length();
        if (len == 0) {
            return (String[]) ArrayUtils.EMPTY_STRING_ARRAY;
        }
        final List<String> list = new ArrayList<String>();
        int sizePlus1 = 1;
        int i = 0;
        int start = 0;
        boolean match = false;
        boolean lastMatch = false;
        if (separatorChars == null) {
            while (i < len) {
                if (Character.isWhitespace(str.charAt(i))) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                }
                else {
                    lastMatch = false;
                    match = true;
                    ++i;
                }
            }
        }
        else if (separatorChars.length() == 1) {
            final char sep = separatorChars.charAt(0);
            while (i < len) {
                if (str.charAt(i) == sep) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                }
                else {
                    lastMatch = false;
                    match = true;
                    ++i;
                }
            }
        }
        else {
            while (i < len) {
                if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                    if (match || preserveAllTokens) {
                        lastMatch = true;
                        if (sizePlus1++ == max) {
                            i = len;
                            lastMatch = false;
                        }
                        list.add(str.substring(start, i));
                        match = false;
                    }
                    start = ++i;
                }
                else {
                    lastMatch = false;
                    match = true;
                    ++i;
                }
            }
        }
        if (match || (preserveAllTokens && lastMatch)) {
            list.add(str.substring(start, i));
        }
        return list.toArray(new String[list.size()]);
    }

    public static String truncateToFitUtf16ByteLength(String s, int maxBytes) {
        if (s == null) {
            return null;
        }
        Charset charset = Charset.forName("UTF-16LE");
        CharsetDecoder decoder = charset.newDecoder();
        byte[] sba = s.getBytes(charset);
        if (sba.length <= maxBytes) {
            return s;
        }
        ByteBuffer bb = ByteBuffer.wrap(sba, 0, maxBytes);
        CharBuffer cb = CharBuffer.allocate(maxBytes);
        decoder.onMalformedInput(CodingErrorAction.IGNORE);
        decoder.decode(bb, cb, true);
        decoder.flush(cb);
        return new String(cb.array(), 0, cb.position());
    }

    public static String truncateToFitUtf8ByteLength(String s, int maxBytes) {
        if (s == null) {
            return null;
        }
        Charset charset = Charset.forName("UTF-8");
        CharsetDecoder decoder = charset.newDecoder();
        byte[] sba = s.getBytes(charset);
        if (sba.length <= maxBytes) {
            return s;
        }
        ByteBuffer bb = ByteBuffer.wrap(sba, 0, maxBytes);
        CharBuffer cb = CharBuffer.allocate(maxBytes);
        decoder.onMalformedInput(CodingErrorAction.IGNORE);
        decoder.decode(bb, cb, true);
        decoder.flush(cb);
        return new String(cb.array(), 0, cb.position());
    }

    public static String truncateToFitByteLength(String s, int maxBytes, final Charset charset) {
        if (s == null) {
            return null;
        }
        CharsetDecoder decoder = charset.newDecoder();
        byte[] sba = s.getBytes(charset);
        if (sba.length <= maxBytes) {
            return s;
        }
        ByteBuffer bb = ByteBuffer.wrap(sba, 0, maxBytes);
        CharBuffer cb = CharBuffer.allocate(maxBytes);
        decoder.onMalformedInput(CodingErrorAction.IGNORE);
        decoder.decode(bb, cb, true);
        decoder.flush(cb);
        return new String(cb.array(), 0, cb.position());
    }

    public static String escapeUnicode(String input) {
    	  StringBuilder b = new StringBuilder(input.length());
    	  Formatter f = new Formatter(b);
    	  for (char c : input.toCharArray()) {
    	    if (c < 128) {
    	      b.append(c);
    	    } else {
    	      f.format("\\u%04x", (int) c);
    	    }
    	  }
    	  f.close();
    	  return b.toString();
    }

    public static String escapeUnicode2(String input) {
  	  StringBuilder b = new StringBuilder(input.length());
  	  for (char c : input.toCharArray()) {
  		  b.append("\\x" + ((int)c));
  	  }
  	  return b.toString();
    }

    public static final String escapeHtml4(final String input) {
        return StringE.ESCAPE_HTML4.translate(input);
    }

    public static final String unescapeHtml4(final String input) {
        return StringE.UNESCAPE_HTML4.translate(input);
    }

    public static String remove(final String str, final String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        return replace(str, remove, "", -1);
    }

    public static String remove(final String str, final char remove) {
        if (isEmpty(str) || str.indexOf(remove) == -1) {
            return str;
        }
        final char[] chars = str.toCharArray();
        int pos = 0;
        for (int i = 0; i < chars.length; ++i) {
            if (chars[i] != remove) {
                chars[pos++] = chars[i];
            }
        }
        return new String(chars, 0, pos);
    }

    public static String replace(final String text, final String searchString, final String replacement) {
        return replace(text, searchString, replacement, -1);
    }

    public static String replace(final String text, final String searchString, final String replacement, int max) {
        if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) {
            return text;
        }
        int start = 0;
        int end = text.indexOf(searchString, start);
        if (end == -1) {
            return text;
        }
        final int replLength = searchString.length();
        int increase = replacement.length() - replLength;
        increase = ((increase < 0) ? 0 : increase);
        increase *= ((max < 0) ? 16 : ((max > 64) ? 64 : max));
        final StringBuilder buf = new StringBuilder(text.length() + increase);
        while (end != -1) {
            buf.append(text.substring(start, end)).append(replacement);
            start = end + replLength;
            if (--max == 0) {
                break;
            }
            end = text.indexOf(searchString, start);
        }
        buf.append(text.substring(start));
        return buf.toString();
    }

    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    public static boolean isNotEmpty(final CharSequence cs) {
        return !isEmpty(cs);
    }

    public static boolean containsIgnoreCase(String src, String what) {
        final int length = what.length();
        if (length == 0)
            return true; // Empty string is contained

        final char firstLo = Character.toLowerCase(what.charAt(0));
        final char firstUp = Character.toUpperCase(what.charAt(0));

        for (int i = src.length() - length; i >= 0; i--) {
            // Quick check before calling the more expensive regionMatches() method:
            final char ch = src.charAt(i);
            if (ch != firstLo && ch != firstUp)
                continue;

            if (src.regionMatches(true, i, what, 0, length))
                return true;
        }

        return false;
    }

    public static String removeEnd(final String str, final String remove) {
        if (isEmpty(str) || isEmpty(remove)) {
            return str;
        }
        if (str.endsWith(remove)) {
            return str.substring(0, str.length() - remove.length());
        }
        return str;
    }

    public static String strip(String str, final String stripChars) {
        if (isEmpty(str)) {
            return str;
        }
        str = stripStart(str, stripChars);
        return stripEnd(str, stripChars);
    }

    public static String stripStart(final String str, final String stripChars) {
        final int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return str;
        }
        int start = 0;
        if (stripChars == null) {
            while (start != strLen && Character.isWhitespace(str.charAt(start))) {
                ++start;
            }
        }
        else {
            if (stripChars.isEmpty()) {
                return str;
            }
            while (start != strLen && stripChars.indexOf(str.charAt(start)) != -1) {
                ++start;
            }
        }
        return str.substring(start);
    }

    public static String stripEnd(final String str, final String stripChars) {
        int end;
        if (str == null || (end = str.length()) == 0) {
            return str;
        }
        if (stripChars == null) {
            while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) {
                --end;
            }
        }
        else {
            if (stripChars.isEmpty()) {
                return str;
            }
            while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != -1) {
                --end;
            }
        }
        return str.substring(0, end);
    }

	public static String filterStringS(final String data, final int max) {
		final FinalBypass<Integer> found = new FinalBypass<Integer>(0);
		final StringBuilder builder = new StringBuilder();
		for (final char f : data.toCharArray()) {
			if (f == ' ') {
				found.add(1);
			}else {
				found.setObject(0);
			}
			if (found.getObject() > max) {
				break;
			}
			if (f == data.charAt(data.length() - 1) && f == ' ') {
				break;
			}else if (f != '\u0000') {
				builder.append(f);
			}
		}
		return builder.toString();
	}

	public static boolean isEqual(final String[] data, final int index, final String search) {
		try {
			return data[index].equals(search);
		}catch (Exception e) {
			return false;
		}
	}
	
	public static boolean isStartsWith(final String[] data, final int index, final String search) {
		try {
			return data[index].startsWith(search);
		}catch (Exception e) {
			return false;
		}
	}
	
	public static boolean isEqualCM(final String[] data, final int index, final String search) {
		try {
			return data[index].replaceAll("\"", "").equals(search);
		}catch (Exception e) {
			return false;
		}
	}

	public static int toInt(final String[] data, final int index) {
		try {
			return Integer.parseInt(data[index]);
		}catch (Exception e) {
			return Integer.MIN_VALUE;
		}
	}

    public static boolean equals(final CharSequence cs1, final CharSequence cs2) {
        if (cs1 == cs2) {
            return true;
        }
        if (cs1 == null || cs2 == null) {
            return false;
        }
        if (cs1 instanceof String && cs2 instanceof String) {
            return cs1.equals(cs2);
        }
        return cs1.length() == cs2.length() && regionMatches(cs1, false, 0, cs2, 0, cs1.length());
    }
    
    public static byte[] getBytes(final String string, final Charset charset) {
        if (string == null) {
            return null;
        }
        return string.getBytes(charset);
    }
    
    public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, final CharSequence substring, final int start, final int length) {
        if (cs instanceof String && substring instanceof String) {
            return ((String)cs).regionMatches(ignoreCase, thisStart, (String)substring, start, length);
        }
        int index1 = thisStart;
        int index2 = start;
        int tmpLen = length;
        final int srcLen = cs.length() - thisStart;
        final int otherLen = substring.length() - start;
        if (thisStart < 0 || start < 0 || length < 0) {
            return false;
        }
        if (srcLen < length || otherLen < length) {
            return false;
        }
        while (tmpLen-- > 0) {
            final char c1 = cs.charAt(index1++);
            final char c2 = substring.charAt(index2++);
            if (c1 == c2) {
                continue;
            }
            if (!ignoreCase) {
                return false;
            }
            if (Character.toUpperCase(c1) != Character.toUpperCase(c2) && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
                return false;
            }
        }
        return true;
    }
    
    private static ByteBuffer getByteBuffer(final String string, final Charset charset) {
        if (string == null) {
            return null;
        }
        return ByteBuffer.wrap(string.getBytes(charset));
    }
    
    public static ByteBuffer getByteBufferUtf8(final String string) {
        return getByteBuffer(string, CharsetUtil.UTF_8);
    }
    
    public static byte[] getBytesIso8859_1(final String string) {
        return getBytes(string, CharsetUtil.ISO_8859_1);
    }
    
    public static byte[] getBytesUnchecked(final String string, final String charsetName) {
        if (string == null) {
            return null;
        }
        try {
            return string.getBytes(charsetName);
        }
        catch (UnsupportedEncodingException e) {
            throw newIllegalStateException(charsetName, e);
        }
    }
    
    public static byte[] getBytesUsAscii(final String string) {
        return getBytes(string, CharsetUtil.US_ASCII);
    }
    
    public static byte[] getBytesUtf16(final String string) {
        return getBytes(string, CharsetUtil.UTF_16);
    }
    
    public static byte[] getBytesUtf16Be(final String string) {
        return getBytes(string, CharsetUtil.UTF_16BE);
    }
    
    public static byte[] getBytesUtf16Le(final String string) {
        return getBytes(string, CharsetUtil.UTF_16LE);
    }
    
    public static byte[] getBytesUtf8(final String string) {
        return getBytes(string, CharsetUtil.UTF_8);
    }
    
    private static IllegalStateException newIllegalStateException(final String charsetName, final UnsupportedEncodingException e) {
        return new IllegalStateException(charsetName + ": " + e);
    }
    
    public static String newString(final byte[] bytes, final Charset charset) {
        return (bytes == null) ? null : new String(bytes, charset);
    }
    
    public static String newString(final byte[] bytes) {
        return (bytes == null) ? null : new String(bytes);
    }
    
    public static String newString(final byte[] bytes, final String charsetName) {
        if (bytes == null) {
            return null;
        }
        try {
            return new String(bytes, charsetName);
        }
        catch (UnsupportedEncodingException e) {
            throw newIllegalStateException(charsetName, e);
        }
    }
    
    public static String newStringIso8859_1(final byte[] bytes) {
        return newString(bytes, CharsetUtil.ISO_8859_1);
    }
    
    public static String newStringUsAscii(final byte[] bytes) {
        return newString(bytes, CharsetUtil.US_ASCII);
    }
    
    public static String newStringUtf16(final byte[] bytes) {
        return newString(bytes, CharsetUtil.UTF_16);
    }
    
    public static String newStringUtf16Be(final byte[] bytes) {
        return newString(bytes, CharsetUtil.UTF_16BE);
    }
    
    public static String newStringUtf16Le(final byte[] bytes) {
        return newString(bytes, CharsetUtil.UTF_16LE);
    }
    
    public static String newStringUtf8(final byte[] bytes) {
        return newString(bytes, CharsetUtil.UTF_8);
    }

	public static String toString(final byte[] toString) {
		return newString(toString, CharsetUtil.getCharset());
	}

	public static String toString(final byte[] toString, final Charset charset) {
		return newString(toString, charset);
	}
	
	static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	static final nSecureRandom rnd = new nSecureRandom();

	public static String randomString(int len){
		final SecureRandom rnd = new SecureRandom();
		StringBuilder sb = new StringBuilder(len);
		for(int i = 0; i < len; i++) 
			sb.append(AB.charAt(rnd.nextInt(AB.length())));
		return sb.toString();
	}

	public static String generateSecure(final int length) {
		return randomString(length);
	}
	
	public static List<String> splitEqually(String text, int size) {
		if (text.length() <= size) {
			final ArrayList<String> temp = new ArrayList<String>();
			temp.add(text);
			return temp;
		}
	    List<String> ret = new ArrayList<String>((text.length() + size - 1) / size);

	    for (int start = 0; start < text.length(); start += size) {
	        ret.add(text.substring(start, Math.min(text.length(), start + size)));
	    }
	    return ret;
	}
	
	public static String[] splitInParts(String s, int partLength)
	{
	    int len = s.length();

	    // Number of parts
	    int nparts = (len + partLength - 1) / partLength;
	    String parts[] = new String[nparts];

	    // Break into parts
	    int offset= 0;
	    int i = 0;
	    while (i < nparts)
	    {
	        parts[i] = s.substring(offset, Math.min(offset + partLength, len));
	        offset += partLength;
	        i++;
	    }

	    return parts;
	}
	
	public static String[] splitEquallyA(String text, int size) {
		return (String[]) ArrayUtils.arrayListToArray((ArrayList) splitEqually(text, size));
	}

	public static String[] byLineSepeartor(final String text) {
		return text.split(Pattern.quote(JVMUtil.lineSep()));
	}

	public static String[] byLineSepeartorAI(String filtered) {
		final ArrayList<String> out = new ArrayList<String>();
		for (final String i : filtered.split(Pattern.quote(JVMUtil.lineSep()))) {
			out.add(i + "_NL_");
		}		
		return ArrayUtils.arrayListToArray(out);
	}

	public static String safeSplit(final String line) {
		final StringBuilder out = new StringBuilder();
		final char[] data = line.toCharArray();
		boolean delnext = false;
		boolean inquote = false;
		for (char x : data) {
			if (!delnext) {
				out.append(x);
			}else {
				delnext = false;
			}
			if (x == '"') {
				if (inquote) {
					out.append("__sp__");
					delnext = true;
				}
				inquote = !inquote;
			}
		}
		
		return out.toString();
	}

	public static List<String> splitEquallyG2D(final String text, final int size, final int split) {
		return splitEqually(text, size / split);
	}
	
    private static List<String> getParts(String string, int partitionSize) {
        List<String> parts = new ArrayList<String>();
        int len = string.length();
        for (int i=0; i<len; i+=partitionSize)
        {
            parts.add(string.substring(i, Math.min(len, i + partitionSize)));
        }
        return parts;
    }

	public static String toID4(final String object) {
		final StringBuilder output = new StringBuilder();
		for (final char c : object.toCharArray()) {
			final int val = (int) c;
			final StringBuilder toadd = new StringBuilder();
			final int v_l = String.valueOf(val).length();
			if (val < 1000) {
				while (toadd.length() < 4 - v_l) {
					toadd.append(0);
				}
				toadd.append(val);
			}else {
				toadd.append(val);
			}
			output.append(toadd);
		}
		return output.toString();
	}
	
	public static String fromID4(final String object) {
		final StringBuilder output = new StringBuilder();
		final String[] vals = splitEquallyA(object, 4);
		for (final String s : vals) {
			output.append((char) (int) Integer.valueOf(s));
		}
//		for (final char c : object.toCharArray()) {
//			final int val = (int) c;
//			final StringBuilder toadd = new StringBuilder();
//			final int v_l = String.valueOf(val).length();
//			if (val < 1000) {
//				while (toadd.length() != v_l) {
//					toadd.append(0);
//				}
//				toadd.append(val);
//			}else {
//				toadd.append(val);
//			}
//			output.append(toadd);
//		}
		return output.toString();
	}

	@Deprecated
	public static String mul(final String str, final int mul) {
		final StringBuilder output = new StringBuilder();
		for (final char c : str.toCharArray()) {
			final int val = (int) c;
			final double times = val * MathUtil.minClamp(1, mul);
			output.append((char) times);
		}
		return output.toString();
	}

	@Deprecated
	public static String mul(final String str, final String mul) {
		final StringBuilder output = new StringBuilder();
		final Integer[] vars = ArrayUtils.listToArray(MathUtil.range(mul.length(), str.length())); //MathUtil.shareOverString(str, mul);
		//ArrayUtils.println(vars);
		final char[] chars = str.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			final char c = chars[i];
			final int val = (int) c;
			final int x = vars[MathUtil.clamp(0, vars.length - 1, i)];
			final double times = val * MathUtil.minClamp(1, x);
			output.append((char) (double) times);
			//System.out.println(val + " > " + times + " [X " + vars[i] + "]");
		}
		return output.toString();
	}
	
	@Deprecated
	public static String unmul(final String str, final String mul) {
		final StringBuilder output = new StringBuilder();
		final Integer[] vars = MathUtil.shareOverString(mul, str);
		final char[] chars = str.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			final char c = chars[i];
			final int val = (int) c;
			//final double times = val / MathUtil.minClamp(1, vars[i]);
			final int x = vars[MathUtil.clamp(0, vars.length - 1, i)];
			final double times = val * MathUtil.minClamp(1, x);
			output.append((char) (double) times);
			//if (times > 2) {
			//	output.append((char) (double) times);
			//}else {
				//log.warn("Incorrect decode found, skipping...");
			//}
			//System.out.println(times + " < " + val + " [/ " + vars[i] + "]");
		}
		return output.toString();
	}
	
	@Deprecated
	public static String unmul(final String str, final int mul) {
		final StringBuilder output = new StringBuilder();
		for (final char c : str.toCharArray()) {
			final int val = (int) c;
			output.append((char) (double) (val / MathUtil.minClamp(1, mul)));
		}
		return output.toString();
	}

	public static String toID(final String object, final int count) {
		final StringBuilder output = new StringBuilder();
		for (final char c : object.toCharArray()) {
			final int val = (int) c;
			final StringBuilder toadd = new StringBuilder();
			final int v_l = String.valueOf(val).length();
			if (val < 1000) {
				while (toadd.length() < count - v_l) {
					toadd.append(0);
				}
				toadd.append(val);
			}else {
				toadd.append(val);
			}
			output.append(toadd);
		}
		return output.toString();
	}
	
	public static String fromID(final String object, final int count) {
		final StringBuilder output = new StringBuilder();
		final String[] vals = splitEquallyA(object, count);
		for (final String s : vals) {
			output.append((char) (int) (Integer.parseInt(s)));
		}
//		for (final char c : object.toCharArray()) {
//			final int val = (int) c;
//			final StringBuilder toadd = new StringBuilder();
//			final int v_l = String.valueOf(val).length();
//			if (val < 1000) {
//				while (toadd.length() < count - v_l) {
//					toadd.append(0);
//				}
//				toadd.append(val);
//			}else {
//				toadd.append(val);
//			}
//			output.append(toadd);
//		}
		return output.toString();
	}

	public static String toIDL(String buffer) {
		final StringBuilder output = new StringBuilder();
		final char[] bu = buffer.toCharArray();
		for (int i = 0; i < bu.length; i++) {
			final char c = bu[i];
			final long val = (long) c;
			output.append(val);
			if (i < bu.length - 1) {
				output.append(",");
			}
		}
		return output.toString();
	}
	
	public static String fromIDL(String buffer) {
		final StringBuilder output = new StringBuilder();
		final String[] data = buffer.split(",");
		for (final String f : data) {
			output.append((char) (long) Long.parseLong(f));
		}
		return output.toString();
	}

	public static String listToAscii(final String string) {
		final StringBuilder output = new StringBuilder();
		final String[] data = string.split(",");
		for (final String f : data) {
			final int i = Integer.parseInt(f);
			output.append(Character.toChars(i + 64));
		}
		return output.toString();
	}
	
	public static String listToSTD(final String string) {
		final StringBuilder output = new StringBuilder();
		final String[] data = string.split(",");
		for (final String f : data) {
			final int i = Integer.parseInt(f);
			output.append(Character.toChars(i));
		}
		return output.toString();
	}

	public static String revM(final String string) {
		final StringBuilder output = new StringBuilder();
		final char[] out = string.toCharArray();
		for (int i = out.length - 1; i > 0; i--) {
			output.append(out[i]);
		}
		return output.toString();
	}
	
	public static String shiftS(final String string, final int toShift) {
		final StringBuilder output = new StringBuilder();
		final char[] data = string.toCharArray();
		for (final char f : data) {
			final int i = (int) f;
			System.out.println(i);
			if (f == ' ') {
				output.append(" ");
			} else if (i >= 64) {
				output.append((char) (i + toShift));
			}
		}
		return output.toString();
	}
	
	public static String alphabet(char letter1, char letter2) { 
	    StringBuffer out = new StringBuffer();    
	    for (char c = letter1; c < letter2; c++) {
	        out.append(c);
	    }
	    return out.toString();
	}
	
	public static String shift(final String s) {
	    return s.charAt(s.length()-1)+s.substring(0, s.length()-1);
	}
	
	public static String shift(final String s, final int amount) {
	    final FinalBypass<String> out = new FinalBypass<String>(s);
	    for (int i = 0; i < amount; i++) {
	    	out.setObject(shift(out.getObject()));
	    }
	    return out.getObject();
	}
	
	public static String shiftString(String string, int shift) {
		   if(Math.abs(shift) > string.length()) return string; //or exception
		   boolean negative = shift != Math.abs(shift);
		   shift = Math.abs(shift);
		   return negative? string.substring(string.length()-shift, string.length()) + string.substring(0, string.length()-shift) : string.substring(shift+1) + string.substring(0, shift);
		}

	public static boolean isUpperCase(final String in) {
		for (char c : in.toCharArray()) {
			if (!Character.isUpperCase(c)) {
				return false;
			}
		}
		return true;
	}
	
	public static boolean isLowerCase(final String in) {
		for (char c : in.toCharArray()) {
			if (!Character.isLowerCase(c)) {
				return false;
			}
		}
		return true;
	}

	public static String toASCII(final String input) {
		final String s1 = Normalizer.normalize(input, Normalizer.Form.NFKD);
		final String regex = "[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+";
		try {
			return new String(s1.replaceAll(regex, "").getBytes("ascii"), "ascii");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return input;
		}
	}

	public static String toStandard(final BigInteger[] split) {
		final StringBuilder out = new StringBuilder();
		for (final BigInteger i : split) {
			out.append((char) i.intValue());
		}
		return out.toString();
	}

	public static String limitSpaces(final String data, final int max) {
		final StringBuilder out = new StringBuilder();
		int spacesHit = 0;
		for (final char x : data.toCharArray()) {
			if (x == ' ') {
				if (spacesHit < max) {
					out.append(x);
				}
				spacesHit += 1;
			}else {
				out.append(x);
				spacesHit = 0;
			}
		}
		return out.toString();
	}
	
	public static String test_linux_parse(final String data) {
		final StringBuilder out = new StringBuilder();
		int spacesHit = 0;
		for (final char x : data.toCharArray()) {
			//x != '\u0073' && 
			if (x != '\u0009') {
				out.append(x);
			}
		}
		return out.toString();
	}
	
	public static String limitTabs(final String data, final int max) {
		final StringBuilder out = new StringBuilder();
		int spacesHit = 0;
		for (final char x : data.toCharArray()) {
			if (x == '\t') {
				if (spacesHit < max) {
					out.append(x);
				}
				spacesHit += 1;
			}else {
				out.append(x);
				spacesHit = 0;
			}
		}
		return out.toString();
	}

	public static String toHex_DC(final byte[] input) {
		return DatatypeConverter.printHexBinary(input);
	}
	
	private static String digits = "0123456789abcdef";

	public static String toHex(byte[] data){
	    StringBuffer buf = new StringBuffer();
	    for (int i = 0; i != data.length; i++)
	    {
	        int v = data[i] & 0xff;
	        buf.append(digits.charAt(v >> 4));
	        buf.append(digits.charAt(v & 0xf));
	    }
	    return buf.toString();
	}

	public static String listToString(final String[] input) {
		if (input == null) {
			log.error("[listToString] input was null! Returning 'N/A'...");
			return "N/A";
		}
		final StringBuilder out = new StringBuilder();
		out.append("{");
		for (int i = 0; i < input.length; i++) {
			final String entry = input[i];
			if (i == input.length - 1) {
				out.append(entry);
			}else {
				out.append(entry);
				out.append(",");
			}
		}
		out.append("}");
		return out.toString();
	}
	
	public static String[] listToStringf(final String in) {
		final StringBuilder out = new StringBuilder();
		final String[] users = in.substring(1, in.length()).split(",");
		return users;
	}

	public static String hash(final String raw) {
		return MathUtil.toHexadecimal(raw.getBytes());
	}
	
	public static String hash(final byte[] raw) {
		return MathUtil.toHexadecimal(raw);
	}

	public static String toStringNL(final String[] sl) {
		final StringBuilder builder = new StringBuilder();
		for (int i = 0; i < sl.length; i++) {
			final String s = sl[i];
			if (i - 1 >= sl.length) {
				builder.append(s);
			}else {
				builder.append(s + JVMUtil.lineSep());
			}
		}
		return builder.toString();
	}

    public static final String toStringA(final byte[] ba, final int offset, final int length) {
        final char[] buf = new char[length * 2];
        int k;
        for (int i = 0, j = 0; i < length; k = ba[offset + i++], buf[j++] = HEX_DIGITS[k >>> 4 & 0xF], buf[j++] = HEX_DIGITS[k & 0xF]) {}
        return new String(buf);
    }
    
    public static String toStringA(final byte[] ba) {
        return toStringA(ba, 0, ba.length);
    }
    
    private static final char[] HEX_DIGITS;
	public static final String NULL_STRING = String.valueOf((char) 10) ;
    static {
    	HEX_DIGITS = "0123456789ABCDEF".toCharArray();
    }
    
	public static String limitCharsAR(final String input, final int limit) {
		if (input == null || limit <= 0) {
			return input;
		}
		if (input.length() < limit) {
			return input;
		}
		return input.substring(0, limit) + "...";
	}
	
	public static String limitChars(final String input, final int limit) {
		if (input == null || limit <= 0) {
			return input;
		}
		if (input.length() < limit) {
			return input;
		}
		return input.substring(0, limit);
	}
	
    /**
     * Tries to turn the given Object into a List of Strings.
     * 
     * If the given Object is a List, go through all items and copy those
     * that are Strings into a new List of Strings.
     * 
     * @param obj
     * @return 
     */
    public static List<String> getStringList(Object obj) {
        List<String> result = new ArrayList<String>();
        if (obj instanceof List) {
            for (Object item : (List)obj) {
                if (item instanceof String) {
                    result.add((String)item);
                }
            }
        }
        return result;
    }
    
    public static String join(String[] array) {
        return join(Arrays.asList(array), ",");
    }
    
    public static String join(Collection<?> items, String delimiter) {
        return join(items, delimiter, -1, -1);
    }
    
    public static String join(Collection<?> items, String delimiter, int start) {
        return join(items, delimiter, start, -1);
    }
    
    public static String join(Collection<?> items, String delimiter, int start, int end) {
        if (items == null || items.isEmpty()) {
            return "";
        }
        start = start > -1 ? start : 0;
        end = end > -1 ? end : items.size();
        
        StringBuilder b = new StringBuilder();
        Iterator<?> it = items.iterator();
        int i = 0;
        while (it.hasNext()) {
            String next = it.next().toString();
            if (i >= start && i < end) {
                b.append(next);
                if (it.hasNext() && i+1 < end) {
                    b.append(delimiter);
                }
            }
            i++;
        }
        return b.toString();
    }
    
    /**
     * Shortens the given {@code input} to the {@code max} length. Only changes
     * the {@code input} if it actually exceeds {@code max} length, but if it
     * does, the returning text is 2 shorter than {@code max} because it also adds
     * ".." where it shortened the text.
     * 
     * Positive {@code max} length values shorten the {@code input} at the end,
     * negative values shorten the {@code input} at the start.
     * 
     * @param input The {@code String} to shorten
     * @param max The maximum length the String should have after this
     * @return The modified {@code String} if {@code input} exceeds the
     * {@code max} length, the original value otherwise
     */
    public static String shortenTo(String input, int max) {
        if (input != null && input.length() > Math.abs(max)) {
            if (max > 2) {
                return input.substring(0, max-2)+"..";
            } else if (max < -2) {
                return ".."+input.substring(input.length() + max + 2 ); // abcd      -3
            } else {
                return "..";
            }
        }
        return input;
    }
    
    public static String shortenTo(String input, int max, int min) {
        if (input != null && input.length() > max) {
            if (min+2 > max) {
                min = max-2;
            }
            if (max > 2) {
                String start = input.substring(0, min);
                String end = input.substring(input.length() - (max - min - 2));
                return start+".."+end;
            } else {
                return "..";
            }
        }
        return input;
    }
    
    public static String trim(String s) {
        if (s == null) {
            return null;
        }
        return s.trim();
    }
    
    public static String nullToString(String s) {
        if (s == null) {
            return "";
        }
        return s;
    }
    
    public static String toLowerCase(String s) {
        return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
    }
    
    /**
     * Removes leading and trailing whitespace and removes and duplicate
     * whitespace in the middle. Due to the way it works, it also replaces any
     * whitespace characters that are not a space with a space (e.g. tabs).
     * 
     * @param s The String
     * @see removeDuplicateWhitespace(String text)
     * @return The modified String or null if the given String was null
     */
    public static String trimAll(String s) {
        if (s == null) {
            return s;
        }
        return removeDuplicateWhitespace(s).trim();
    }
    
    private static final Pattern WHITESPACE = Pattern.compile("\\s+");
    
    /**
     * Replaces all occurences of one or more consecutive whitespace characters
     * with a single space. So it also replaces any whitespace characters that
     * are not a space with a space (e.g. tabs).
     * 
     * @param text
     * @return 
     */
    public static String removeDuplicateWhitespace(String text) {
        return WHITESPACE.matcher(text).replaceAll(" ");
    }
    
    private static final Pattern LINEBREAK_CHARACTERS = Pattern.compile("[\\r\\n]+");
    
    /**
     * Removes any linebreak characters from the given String and replaces them
     * with a space. Consecutive linebreak characters are replaced with only a
     * single space.
     * 
     * @param s The String (can be empty or null)
     * @return The modified String or null if the given String was null
     */
    public static String removeLinebreakCharacters(String s) {
        if (s == null) {
            return null;
        }
        return LINEBREAK_CHARACTERS.matcher(s).replaceAll(" ");
    }
    
    public static String append(String a, String sep, String b) {
        if (a == null || a.isEmpty()) {
            return b;
        }
        if (b == null || b.isEmpty()) {
            return a;
        }
        return a+sep+b;
    }
    
    /**
     * Checks if any of the String arguments is null or empty.
     * 
     * @param input A number of String arguments
     * @return true if at least one of the arguments is null or empty, false
     * otherwise
     */
    public static boolean isNullOrEmpty(String... input) {
        if (input == null) {
            return true;
        }
        for (String s : input) {
            if (s == null || s.isEmpty()) {
                return true;
            }
        }
        return false;
    }
    
    public static final String UTF8_BOM = "\uFEFF";
    
    /**
     * Remove the UTF-8 BOM frmo the beginning of the input.
     * 
     * @param input
     * @return 
     */
    public static String removeUTF8BOM(String input) {
        if (input != null && input.startsWith(UTF8_BOM)) {
            return input.substring(1);
        }
        return input;
    }
    
    public static String join(final Object[] array, final String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }
    
    public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = "";
        }
        final int noOfItems = endIndex - startIndex;
        if (noOfItems <= 0) {
            return "";
        }
        final StringBuilder buf = new StringBuilder(noOfItems * 16);
        for (int i = startIndex; i < endIndex; ++i) {
            if (i > startIndex) {
                buf.append(separator);
            }
            if (array[i] != null) {
                buf.append(array[i]);
            }
        }
        return buf.toString();
    }
    
    public static String join(final Iterator<?> iterator, final char separator) {
        if (iterator == null) {
            return null;
        }
        if (!iterator.hasNext()) {
            return "";
        }
        final Object first = iterator.next();
        if (!iterator.hasNext()) {
            return String.valueOf(first);
        }
        final StringBuilder buf = new StringBuilder(256);
        if (first != null) {
            buf.append(first);
        }
        while (iterator.hasNext()) {
            buf.append(separator);
            final Object obj = iterator.next();
            if (obj != null) {
                buf.append(obj);
            }
        }
        return buf.toString();
    }
    
    public static String join(final Iterator<?> iterator, final String separator) {
        if (iterator == null) {
            return null;
        }
        if (!iterator.hasNext()) {
            return "";
        }
        final Object first = iterator.next();
        if (!iterator.hasNext()) {
            return String.valueOf(first);
        }
        final StringBuilder buf = new StringBuilder(256);
        if (first != null) {
            buf.append(first);
        }
        while (iterator.hasNext()) {
            if (separator != null) {
                buf.append(separator);
            }
            final Object obj = iterator.next();
            if (obj != null) {
                buf.append(obj);
            }
        }
        return buf.toString();
    }
    
    public static String join(final Iterable<?> iterable, final char separator) {
        if (iterable == null) {
            return null;
        }
        return join(iterable.iterator(), separator);
    }
    
    public static String join(final Iterable<?> iterable, final String separator) {
        if (iterable == null) {
            return null;
        }
        return join(iterable.iterator(), separator);
    }

	public static String format(final String format, Object[] objects) {
		String formato = format;
		for (final Object o : objects) {
			formato = format.replace("{}", String.valueOf(o));
		}
		return formato;
	}

	public static boolean isNumeric(final String text) {
		try {
			Integer.parseInt(text);
			return true;
		}catch (Exception e) {
			return false;
		}
	}
	
	public static boolean containsIgnoreCase0(String str, String searchStr)  {
	    if(str == null || searchStr == null) return false;

	    final int length = searchStr.length();
	    if (length == 0)
	        return true;

	    for (int i = str.length() - length; i >= 0; i--) {
	        if (str.regionMatches(true, i, searchStr, 0, length))
	            return true;
	    }
	    return false;
	}

	public static int __calc_shifter(final String password) {
		int out = 0;
		for (final char c : password.toCharArray()) {
			out += c;
		}
		if (out >= 65535) {
			out /= 10;
		}
		return out;
	}
	
	public final static String[] zeroPad00 = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09" };
	
	public static String zeroPad2(int x) {
        if (x >= 0 && x < 10) {
            return zeroPad00[x];
        }
        return "" + x;
	}

	public static boolean isEqual(final String value, final String string) {
		if (value == null || string == null) {
			return false;
		}
		return value.equals(string);
	}

	public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) {
        if (str == null || searchStr == null) {
            return -1;
        }
        if (startPos < 0) {
            startPos = 0;
        }
        int endLimit = str.length() - searchStr.length() + 1;
        if (startPos > endLimit) {
            return -1;
        }
        if (searchStr.length() == 0) {
            return startPos;
        }
        for (int i = startPos; i < endLimit; i++) {
            if (regionMatches(str, true, i, searchStr, 0, searchStr.length())) {
                return i;
            }
        }
        return -1;
    }
	
	/**
     * <p>Finds the last index in the {@code CharSequence} that matches the
     * specified character.</p>
     *
     * @param cs  the {@code CharSequence} to be processed
     * @param searchChar  the char to be searched for
     * @param start  the start index, negative returns -1, beyond length starts at end
     * @return the index where the search char was found, -1 if not found
     */
    public static int lastIndexOf(CharSequence cs, int searchChar, int start) {
        if (cs instanceof String) {
            return ((String) cs).lastIndexOf(searchChar, start);
        } else {
            int sz = cs.length();
            if (start < 0) {
                return -1;
            }
            if (start >= sz) {
                start = sz - 1;
            }
            for (int i = start; i >= 0; --i) {
                if (cs.charAt(i) == searchChar) {
                    return i;
                }
            }
            return -1;
        }
    }
}
